"""游戏核心模块"""
import random

import astropy.utils.console
import pygame
from game_items import *
from game_hud import *
from game_music import *


class Game(object):
    """游戏核心类"""

    def __init__(self):
        # 游戏窗口
        self.main_window = pygame.display.set_mode(SCREEN_RECT.size)
        pygame.display.set_caption('飞机大战')

        # 游戏状态
        self.is_game_over = False  # 结束
        self.is_game_pause = False  # 暂停

        # 游戏精灵组
        self.all_group = pygame.sprite.Group()  # 存放所有界面上的精灵
        self.enemies_group = pygame.sprite.Group()  # 敌机精灵组
        self.supplies_group = pygame.sprite.Group()  # 道具精灵组

        # 游戏精灵
        # 用两个背景交替显示,两种写法
        # Background(False, self.all_group)  # 背景精灵1
        # Background(True, self.all_group)  # 背景精灵2
        self.all_group.add(Background(False), Background(True))  # 加入两个背景精灵

        # 先创建控制面板
        self.hud_panel = HUDPanel(self.all_group)

        # 再创建用户精灵
        self.hero_sprite = Hero(self.all_group)
        self.hud_panel.show_bomb(self.hero_sprite.bomb_cout)

        # 初始化敌机
        self.create_enemies()

        # 初始化道具
        self.creat_supply()

        # 音乐播放器
        self.player = MusicPlayer('game_music.ogg')
        self.player.play_music()

        """
        pygame.mixer.music模块的常用方法，适用于长音乐
        load(音乐文件路径)
        play(循环次数)：-1表示一直循环播放，1表示播放一次
        stop()停止
        pause()暂停
        unpause()取消暂停，继续播放
        set_volume()设置音量0-1之间
        
        pygame.mixer有一个Sound类，适用于音效的短音频
        sound=Sound(音效文件路径)
        sound.play()播放
        sound.stop()停止
        sound.set_volume()设置音量0-1
        """
        pygame.mixer.music.load('res/sound/game_music.ogg')
        pygame.mixer.music.play(-1)  # 无限循环背景音乐

        # 音效
        hero_down_sound = pygame.mixer.Sound('res/sound/me_down.wav')
        hero_down_sound.play()

    def reset_game(self):
        """游戏重置"""
        self.is_game_over = False
        self.is_game_pause = False

        # 重置面板
        self.hud_panel.reset_panel()

        # 每次重新开始游戏，重置玩家位置
        self.hero_sprite.rect.midbottom = HERO_DEFAULT_MID_BOTTOM

        # 销毁所有敌人飞机
        for enemy in self.enemies_group:
            enemy.kill()

        # 销毁所有子弹
        for bullet in self.hero_sprite.bullets_group:
            bullet.kill()

        # 重新创建飞机
        self.create_enemies()

    def start(self):
        """开始游戏"""
        # 创建时钟
        clock = pygame.time.Clock()

        # 动画帧数计数器
        frame_count = 0

        while True:
            # 判断是否已经死亡
            self.is_game_over = self.hud_panel.lives_count == 0

            # 处理事件监听,返回Ture说明要退出游戏
            if self.event_handler():
                # 退出游戏时保存最好成绩
                self.hud_panel.save_best_score()
                return

            # 根据游戏状态切换显示内容
            if self.is_game_over:
                # print("游戏已经结束，按空格继续...")
                self.hud_panel.panel_paused(True, self.all_group)
            elif self.is_game_pause:
                # print("游戏已经暂停，按空格继续...")
                self.hud_panel.panel_paused(False, self.all_group)
            else:

                self.hud_panel.panel_resume(self.all_group)

                # 处理长按键
                keys = pygame.key.get_pressed()  # get_pressed()得到一个元组，每个按键有独一无二的下标，按下为1
                move_hor = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]  # 水平移动基数
                move_ver = keys[pygame.K_DOWN] - keys[pygame.K_UP]  # 垂直移动基数

                # 检测碰撞
                self.chack_collide()

                frame_count = (frame_count + 1) % FRAME_INTERVAL
                self.all_group.update(frame_count == 0, move_hor, move_ver)  # 更改某些元素

            # 在窗口中绘制内容
            self.all_group.draw(self.main_window)

            # 刷新整个界面
            pygame.display.update()
            # 设置刷新率60帧
            clock.tick(60)

    def event_handler(self):
        """获取并处理事件"""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                # 点击退出游戏
                return True
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                # 按下ESC键退出游戏
                return True
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                # 按下空格键
                if self.is_game_over:  # 游戏结束，重新开始
                    self.reset_game()
                else:
                    # 游戏还没结束则暂停
                    self.is_game_pause = not self.is_game_pause
                    self.player.pause_music(self.is_game_pause)  # 背景音乐切换

            # 当游戏进行时可以进行的操作
            if not self.is_game_over and not self.is_game_pause:
                if event.type == pygame.KEYDOWN and event.key == pygame.K_b:
                    # 使用炸弹，修改炸弹数量
                    if self.hero_sprite.hp > 0 and self.hero_sprite.bomb_cout > 0:
                        self.player.play_sound('use_bomb.wav')

                    score = self.hero_sprite.blowup(self.enemies_group)
                    self.hud_panel.show_bomb(self.hero_sprite.bomb_cout)
                    if self.hud_panel.increase_score(score):
                        self.create_enemies()

                elif event.type == HERO_DEAD_EVENT:
                    # 玩家死亡
                    self.hud_panel.lives_count -= 1
                    self.hud_panel.show_lives()
                    self.hud_panel.show_bomb(self.hero_sprite.bomb_cout)

                elif event.type == HERO_POWER_OFF_EVENT:
                    # 取消无敌
                    self.hero_sprite.is_power = False
                    pygame.time.set_timer(HERO_POWER_OFF_EVENT, 0)  # 取消无敌之后取消定时器
                elif event.type == HERO_FIRE_EVENT:
                    # 玩家发射子弹
                    self.player.play_sound('bullet.wav')
                    self.hero_sprite.fire(self.all_group)
                elif event.type == THROW_SUPPLY_EVENT:
                    # 投放道具
                    self.player.play_sound('supply.wav')
                    supply = random.choice(self.supplies_group.sprites())
                    supply.throw_supply()
                elif event.type == BULLET_ENHANCED_OFF_EVENT:
                    # 取消子弹加强
                    self.hero_sprite.bullets_kind = 0
                    pygame.time.set_timer(BULLET_ENHANCED_OFF_EVENT, 0)

        return False

    def create_enemies(self):
        """创建敌机"""
        count = len(self.enemies_group.sprites())
        groups = (self.all_group, self.enemies_group)

        # 根据不同关卡创建不同敌机
        if self.hud_panel.level == 1 and count == 0:
            # 关卡1
            for i in range(16):
                Enemy(0, 3, *groups)

        elif self.hud_panel.level == 2 and count == 16:
            # 关卡2
            for enemy in self.enemies_group.sprites():
                enemy.max_speed = 3

            for i in range(8):
                Enemy(0, 3, *groups)
            for i in range(2):
                Enemy(1, 1, *groups)

        elif self.hud_panel.level == 3 and count == 26:
            # 关卡3
            for enemy in self.enemies_group.sprites():
                enemy.max_speed = 3 if enemy.kind == 0 else 3

            for i in range(8):
                Enemy(0, 3, *groups)
            for i in range(2):
                Enemy(1, 3, *groups)
            for i in range(2):
                Enemy(2, 1, *groups)

    def chack_collide(self):
        """检查碰撞"""

        """
        sprite中有两个碰撞检测方法:
        某个精灵与某个精灵组之间的检测:spritecollide(sprite,group,dokill,collided)
            sprite 要检测的精灵
            group 要检测的精灵组精灵组
            dokill 为True是移除与sprite碰撞的group的精灵
            collided 用来检测碰撞的函数，是None就用精灵的rect属性检测是否碰撞,即用图片框，可传入pygame.sprite.collide_mask()方法，使用高级检测，颜色像素块碰撞
        返回一个group中与sprite碰撞的精灵列表
        
        两个精灵组之间的碰撞检测：groupcollide(group1,group2,dokill1,dokill2,collided)
        返回一个字典{key:value}key是group1的精灵，value是group2的精灵
        """

        collide_enemies = pygame.sprite.spritecollide(self.hero_sprite, self.enemies_group, False,
                                                      pygame.sprite.collide_mask)
        # 只让活的飞机有碰撞
        collide_enemies = list(filter(lambda x: x.hp > 0, collide_enemies))

        # 检测非无敌时间时
        if not self.hero_sprite.is_power:
            # 撞毁玩家
            if collide_enemies:
                self.player.play_sound(self.hero_sprite.wav_name)
                self.hero_sprite.hp = 0

        # 撞毁敌人
        for enemy in collide_enemies:
            enemy.hp = 0

        # 子弹和敌机碰撞
        hit_enemies = pygame.sprite.groupcollide(self.enemies_group, self.hero_sprite.bullets_group,
                                                 False, False, pygame.sprite.collide_mask)

        for enemy in hit_enemies:
            # 已经被摧毁的飞机不需要再处理
            if enemy.hp < 0:
                continue

            # 销毁子弹，修改敌机生命值
            for bullet in hit_enemies[enemy]:
                bullet.kill()
                enemy.hp -= bullet.damage

                # 飞机未销毁
                if enemy.hp > 0:
                    continue

                # 飞机销毁，修改分数等级
                if self.hud_panel.increase_score(enemy.value):
                    self.player.play_sound('upgrade.wav')
                    self.create_enemies()

                # 飞机销毁，不需要再便利子弹
                self.player.play_sound(enemy.wav_name)
                break

        # 玩家和道具的碰撞
        supplies = pygame.sprite.spritecollide(self.hero_sprite, self.supplies_group,
                                    False, pygame.sprite.collide_mask)
        if supplies:
            supply = supplies[0]
            self.player.play_sound(supply.wav_name)

            # 根据道具不同产生不同行为
            if supply.kind == 0:
                self.hero_sprite.bomb_cout += 1
                self.hud_panel.show_bomb(self.hero_sprite.bomb_cout)

            else:
                self.hero_sprite.bullets_kind = 1
                pygame.time.set_timer(BULLET_ENHANCED_OFF_EVENT, 15000)

            # 移动道具到屏幕之下
            supply.rect.y = SCREEN_RECT.h

    def creat_supply(self):
        """初始化两个道具，并开始投放定时器"""
        Supply(0, self.all_group, self.supplies_group)
        Supply(1, self.all_group, self.supplies_group)

        pygame.time.set_timer(THROW_SUPPLY_EVENT, 2000)


if __name__ == '__main__':
    # 初始化游戏
    pygame.init()

    # 开始游戏
    Game().start()

    # 结束游戏释放资源
    pygame.quit()
